#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>

using namespace std;

const int INF = -1u >> 1;
const int ITEM_WIDTH = 8;
const int TIPS_WIDTH = 16;

// dijkstra算法采用邻接矩阵记录信息
vector<vector<int>> edges;

vector<int> dist;
vector<bool> visited;

// 递归式dijkstra
void recursive_dijkstra(int source) {
    // 设置当前点已访问
    visited[source] = true;
    // 初始化当前点到其他点的距离
    for (int i = 0; i < edges.size(); ++i) {
        // 当前节点到目标节点无路径，跳过
        if (edges[source][i] == INF) continue;
        // 目标节点已访问过，跳过处理该节点
        if (visited[i]) continue;
        // 确认当前路径需要更新
        if (dist[i] > dist[source] + edges[source][i])
            dist[i] = dist[source] + edges[source][i];
    }
    // 查找下一个源
    int index = -1, min = INF;
    for (int i = 0; i < edges.size(); ++i) {
        // 选取未访问且路径更短的节点
        if (!visited[i] && dist[i] < min) {
            min = dist[i];
            index = i;
        }
    }
    // 无待处理的源，结束递归
    if (index == -1) return;
    recursive_dijkstra(index);
}

// 队列式dijkstra
void queue_dijkstra(int source) {
    // 存储待处理的点
    queue<int> q;

    // 推入第一个点进行处理
    q.push(source);
    while (!q.empty()) {
        // 获取队列中第一个元素
        int u = q.front();
        // 已使用，去掉该元素
        q.pop();
        // 当前节点已访问
        visited[u] = true;
        // 初始化当前点到其他点的距离
        for (int i = 0; i < edges.size(); ++i) {
            // 当前节点到目标节点无路径，跳过
            if (edges[u][i] == INF) continue;
            // 目标节点已访问过，跳过处理该节点
            if (visited[i]) continue;
            // 确认当前路径需要更新
            if (dist[i] > dist[u] + edges[u][i])
                dist[i] = dist[u] + edges[u][i];
        }
        // 查找下一个源
        int index = -1, min = INF;
        for (int i = 0; i < edges.size(); ++i) {
            // 选取未访问且路径更短的节点
            if (!visited[i] && dist[i] < min) {
                min = dist[i];
                index = i;
            }
        }
        // 推入下一个点进行处理，若无需要处理的点则因队列元素用尽结束循环
        if (index != -1) q.push(index);
    }
}

int main() {
    // n: 节点数, m: 边数
    int n, m;
    int from, to;
    cin >> n >> m;
    edges.resize(n);
    dist.resize(n, INF);
    visited.resize(n, false);
    for (int i = 0; i < n; i++) {
        edges[i].resize(n, INF);
    }
    for (int i = 0; i < m; i++) {
        cin >> from >> to;
        edges[from - 1][to - 1] = 1;
    }
    cout << "Adjacency matrix: " << endl;
    cout << setw(ITEM_WIDTH) << "Node ID";
    for (int i = 0; i < n; i++) {
        cout << setw(ITEM_WIDTH) << i + 1;
    }
    cout << endl;
    for (int i = 0; i < n; i++) {
        vector<int> line = edges[i];
        cout << setw(ITEM_WIDTH) << i + 1;
        for (auto &item: line) {
            cout << setw(ITEM_WIDTH);
            if (item == INF) cout << "INF";
            else cout << item;
        }
        cout << endl;
    }
    dist[0] = 0;
    queue_dijkstra(0);
    cout << endl << endl << setw(TIPS_WIDTH) << "From Node 0 To:";
    for (int i = 0; i < n; i++) {
        cout << setw(ITEM_WIDTH) << i + 1;
    }
    cout << endl;
    cout << setw(TIPS_WIDTH) << "Shortest Path:";
    for (auto &item: dist) {
        cout << setw(ITEM_WIDTH);
        if (item == INF) cout << "INF";
        else cout << item;
    }
    cout << endl;
    cout << setw(TIPS_WIDTH) << "Is Final:";
    for (bool item: visited) {
        cout << setw(ITEM_WIDTH);
        cout << (item ? "true" : "false");
    }
}
